package ua.naiksoftware.stompclientexample.slice;

import static ua.naiksoftware.stompclientexample.RestClient.OHOS_EMULATOR_LOCALHOST;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import io.reactivex.CompletableTransformer;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.components.Text;
import ohos.agp.text.Font;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ua.naiksoftware.stomp.Stomp;
import ua.naiksoftware.stomp.StompClient;
import ua.naiksoftware.stomp.dto.StompHeader;
import ua.naiksoftware.stompclientexample.EchoModel;
import ua.naiksoftware.stompclientexample.ResourceTable;
import ua.naiksoftware.stompclientexample.RestClient;
import ua.naiksoftware.stompclientexample.SimpleAdapter;
import ua.naiksoftware.stompclientexample.rxHarmony.OpenHarmonySchedulers;
import ua.naiksoftware.stompclientexample.utils.ToastUtil;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * MainAbilitySlice
 *
 * @since 2021-04-27
 */
public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener {
    static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MainAbilitySlice");
    private static final int NUM_1000 = 1000;
    private static final int NUM_1900 = 1600;
    private static final int NUM_1700 = 1700;
    private static final int NUM_16 = 9;
    private static final int NUM_6 = 19;
    private static final int NUM_5 = 15;
    private static final String LOGIN = "login";
    private static final String PASSCODE = "passcode";
    private SimpleAdapter mAdapter;
    private List<String> mDataSet = new ArrayList<>();
    private StompClient mStompClient;
    private Disposable mRestPingDisposable;
    private final SimpleDateFormat mTimeFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
    private Text text;
    private ListContainer mRecyclerView;
    private Button mBtConnectStomp;
    private Button mBtDisconnetStomp;
    private Button mBtEchotStomp;
    private Button mBtEchoRest;
    private Gson mGson = new GsonBuilder().create();
    private CompositeDisposable compositeDisposable;
    private Display display;

    /**
     * onStart
     *
     * @param intent intent
     */
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        mRecyclerView = (ListContainer) findComponentById(ResourceTable.Id_recycler_view);
        mBtConnectStomp = (Button) findComponentById(ResourceTable.Id_bt_connect_stomp);
        mBtDisconnetStomp = (Button) findComponentById(ResourceTable.Id_bt_disconnect_stomp);
        mBtEchotStomp = (Button) findComponentById(ResourceTable.Id_bt_echo_stomp);
        mBtEchoRest = (Button) findComponentById(ResourceTable.Id_bt_echo_rest);
        text = (Text) findComponentById(ResourceTable.Id_tv_title);
        mBtConnectStomp.setClickedListener(this);
        mBtDisconnetStomp.setClickedListener(this);
        mBtEchotStomp.setClickedListener(this);
        mBtEchoRest.setClickedListener(this);
        mBtConnectStomp.setFont(Font.DEFAULT_BOLD);
        mBtDisconnetStomp.setFont(Font.DEFAULT_BOLD);
        mBtEchotStomp.setFont(Font.DEFAULT_BOLD);
        mBtEchoRest.setFont(Font.DEFAULT_BOLD);
        mAdapter = new SimpleAdapter(mDataSet, getContext());
        mRecyclerView.setItemProvider(mAdapter);
        mStompClient = Stomp.over(Stomp.ConnectionProvider.OKHTTP, "ws://" + OHOS_EMULATOR_LOCALHOST
                + ":" + RestClient.SERVER_PORT + "/example-endpoint/websocket");
        resetSubscriptions();
        text.setFont(Font.DEFAULT_BOLD);
        display = DisplayManager.getInstance().getDefaultDisplay(this).get();
    }

    private void resetSubscriptions() {
        if (compositeDisposable != null) {
            compositeDisposable.dispose();
        }
        compositeDisposable = new CompositeDisposable();
    }

    /**
     * Component
     *
     * @param component component
     */
    @Override
    public void onClick(Component component) {
        switch (component.getId()) {
            case ResourceTable.Id_bt_connect_stomp:
                connectStomp();
                break;
            case ResourceTable.Id_bt_disconnect_stomp:
                disconnectStomp();
                break;
            case ResourceTable.Id_bt_echo_stomp:
                sendEchoViaStomp();
                break;
            case ResourceTable.Id_bt_echo_rest:
                sendEchoViaRest();
                break;
            default:
                break;
        }
    }

    /**
     * onActive
     */
    @Override
    public void onActive() {
        super.onActive();
    }

    /**
     * onForeground
     *
     * @param intent intent
     */
    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    /**
     * 断开连接
     */
    public void disconnectStomp() {
        boolean disconnect = mStompClient.disconnect();
        if (disconnect) {
            toast("Stomp connection closed");
        }
    }

    /**
     * 启动链接
     */
    public void connectStomp() {

        List<StompHeader> headers = new ArrayList<>();
        headers.add(new StompHeader(LOGIN, "guest"));
        headers.add(new StompHeader(PASSCODE, "guest"));

        mStompClient.withClientHeartbeat(NUM_1000).withServerHeartbeat(NUM_1000);

        resetSubscriptions();

        debug("connectStomp");

        Disposable dispLifecycle = mStompClient.lifecycle()
                .subscribeOn(Schedulers.io())
                .observeOn(OpenHarmonySchedulers.mainThread())
                .subscribe(lifecycleEvent -> {
                    switch (lifecycleEvent.getType()) {
                        case OPENED:
                            toast("Stomp connection opened");
                            break;
                        case ERROR:
                            debug("Stomp connection error");
                            toast("Stomp connection  error");
                            break;
                        case CLOSED:
                            toast("Stomp connection closed");
                            resetSubscriptions();
                            break;
                        case FAILED_SERVER_HEARTBEAT:
                            toast("Stomp failed server heartbeat");
                            break;
                        default:
                            break;
                    }
                });

        compositeDisposable.add(dispLifecycle);

        // Receive greetings
        Disposable dispTopic = mStompClient.topic("/topic/greetings")
                .subscribeOn(Schedulers.io())
                .observeOn(OpenHarmonySchedulers.mainThread())
                .subscribe(topicMessage -> {
                    debug("Received " + topicMessage.getPayload());
                    addItem(mGson.fromJson(topicMessage.getPayload(), EchoModel.class));
                    notifyList(mDataSet.size() - 1);
                }, throwable -> {
                    debug("Error on subscribe topic" + throwable);
                });

        compositeDisposable.add(dispTopic);

        mStompClient.connect(headers);

        debug("connectStomp end ");

    }

    /**
     * 发送消息
     */
    public void sendEchoViaStomp() {
        compositeDisposable.add(mStompClient.send("/topic/hello-msg-mapping",
                "Echo STOMP " + mTimeFormat.format(new Date()))
                .compose(applySchedulers())
                .subscribe(() -> {
                    notifyList(mDataSet.size());
                    debug("STOMP echo send successfully");
                }, throwable -> {
                    debug("Error send STOMP echo" + throwable);
                    toast(throwable.getMessage() + "Message  Connect ERROR");
                }));
    }

    /**
     * 重连
     */
    public void sendEchoViaRest() {
        mRestPingDisposable = RestClient.getInstance().getExampleRepository()
                .sendRestEcho("Echo REST " + mTimeFormat.format(new Date()))
                .compose(applySchedulers())
                .subscribe(() -> {

                    debug("REST echo send successfully");
                }, throwable -> {
                    debug("Error send REST echo" + throwable);
                    toast(throwable.getMessage());
                });
    }

    /**
     * addItem
     *
     * @param echoModel echoModel
     */
    private void addItem(EchoModel echoModel) {
        mDataSet.add(echoModel.getEcho() + " - " + mTimeFormat.format(new Date()));
        notifyList(mDataSet.size());
        if (mDataSet.size() > NUM_16) {
            mRecyclerView.setHeight(display.getAttributes().height / NUM_6 * NUM_5);
        }
    }

    private void notifyList(int pos) {
        mRecyclerView.scrollBy(0, NUM_1700);
        mRecyclerView.scrollTo(pos);
        mAdapter.notifyDataSetItemChanged(pos);
    }

    /**
     * toast
     *
     * @param string string
     */
    private void toast(String string) {
        debug(string);
        ToastUtil.toast(this, string);
    }

    /**
     * applySchedulers
     *
     * @return CompletableTransformer
     */
    protected CompletableTransformer applySchedulers() {

        return upstream -> upstream
                .unsubscribeOn(Schedulers.newThread())
                .subscribeOn(Schedulers.io())
                .observeOn(OpenHarmonySchedulers.mainThread());
    }

    /**
     * onStop
     */
    @Override
    protected void onStop() {
        mStompClient.disconnect();
        if (mRestPingDisposable != null) {
            mRestPingDisposable.dispose();
        }
        if (compositeDisposable != null) {
            compositeDisposable.dispose();
        }
        super.onStop();
    }

    /**
     * debug
     *
     * @param msg
     */
    private void debug(String msg) {
        HiLog.error(LABEL, msg, 0);
    }
}
